import annotations.Queryable
import org.springframework.beans.factory.InitializingBean
import types.Hit

/**
 * simple service to lookup data from the database
 */
class LookupService implements InitializingBean {

  boolean transactional = true

  def grailsApplication
  def setting

  void afterPropertiesSet() { this.setting = grailsApplication.config.setting }

/**
 * can return 0 - n
 */
  @Queryable(name = "smiles")
  Collection<Compound> lookupBySmile(def value, Map params = [:]) {
    if (value instanceof List){
      return Compound.executeQuery("Select a from Compound a, Smiles b where a.id = b.compound.id and b.code in (:varList) ", ["varList" : value],params);
    }
    else{
      return Compound.executeQuery("Select a from Compound a, Smiles b where a.id = b.compound.id and b.code = ?", [value],params)
    }
  }

/**
 * can return 0 - n
 */
  @Queryable(name = "kegg")
  Collection<Compound> lookupByKegg(def value, Map params = [:]) {

    if (value instanceof Collection){
      def res = Compound.executeQuery("Select a from Compound a, Kegg b where a.id = b.compound.id and b.keggId in (:varList) ", ["varList" : value],params);

      return res
    }
    else{
       return Compound.executeQuery("Select a from Compound a, Kegg b where a.id = b.compound.id and b.keggId = ?", [value],params)
    }
  }

/**
 * can return 0 - n
 */
  @Queryable(name = "cid")
  Collection<Compound> lookupByCID(def value, Map params = [:]) {

    if (value instanceof List){
      return Compound.executeQuery("Select a from Compound a, PubchemCompound b where a.id = b.compound.id and b.cid in (:varList) ", ["varList" : value],params)
    }
    else{
      Integer v = null

      if (value instanceof Integer == false) {
        v = Integer.parseInt(value.toString())
      }
      else {
        v = value
      }
      return Compound.executeQuery("Select a from Compound a, PubchemCompound b where a.id = b.compound.id and b.cid = ?", [v],params)
    }
  }

/**
 * can return 0-n
 */
  @Queryable(name = "sid")
  Collection<Compound> lookupBySID(def value, Map params = [:]) {

    if (value instanceof List){
          return Compound.executeQuery("Select a from Compound a, PubchemSubstance b where a.id = b.compound.id and b.sid in (:varList) ", ["varList" : value],params)
    }
    else{
      Integer v = null
      if (value instanceof Integer == false) {
        v = Integer.parseInt(value.toString())
      }
      else {
        v = value
      }
      return Compound.executeQuery("Select a from Compound a, PubchemSubstance b where a.id = b.compound.id and b.sid = ?", [v],params)
    }
   }

/**
 * can return 0-n
 */
  @Queryable(name = "cas")
  Collection<Compound> lookupByCas(def value, Map params = [:]) {
    if (value instanceof List){
          return Compound.executeQuery("Select a from Compound a, Cas b where a.id = b.compound.id and b.casNumber in (:varList) ", ["varList" : value],params);
     }
    else {
          return Compound.executeQuery("Select a from Compound a, Cas b where a.id = b.compound.id and b.casNumber = ?", [value],params)
     }
  }

/**
 * can return 0 - 1
 */
  @Queryable(name = "compound")
  Compound lookupByCompoundId(def value, Map params = [:]) {
    Long v = null
    if (value instanceof Long == false) {
      v = Long.parseLong(value.toString())
    }
    else {
      v = value
    }
    return Compound.get(v)
  }

/**
 * can return 0 - 1
 */
  @Queryable(name = "inchi")
  Compound lookupByInchi(def value, Map params = [:]) {

    if (value instanceof List){
          return Compound.find("from Compound a where a.inchi in (:varList) ", ["varList" : value],params)
    }
    else{
       return Compound.find("from Compound a where a.inchi = ?", [value],params)
    }
  }

  /**
   * can return 0 - 1
   */
  @Queryable(name = "formula")
  Collection<Compound> lookupByFormula(def value, Map params = [:]) {
    
    if (value instanceof List){
          return Compound.findAll("from Compound a where a.formula in (:varList) ", ["varList" : value],params)
    }
    else{
      return Compound.findAll("from Compound a where a.formula = ?", [value],params)
    }
  }

  /**
   * can return 0 - 1
   */
  @Queryable(name = "mass")
  Collection<Compound> lookupByExactMass(def value, Map params = [:]) {

    if (value instanceof List){
          return Compound.findAll("from Compound a where a.exactMolareMass in (:varList) ", ["varList" : value],params)
    }
    else{    
      Double v = null
      if (value instanceof Double == false) {
        v = Double.parseDouble(value.toString())
      }
      else {
        v = value
      }
      return Compound.findAll("from Compound a where a.exactMolareMass = ?", [v],params)
    }
  }

/**
 * can return 0 - 1
 */
  @Queryable(name = "inchikey")
  Compound lookupByInchiKey(def value, Map params = [:]) {
    if (value instanceof List){
        return Compound.find("from Compound a where a.inchiHashKey.completeKey in (:varList) ", ["varList" : value],params);
     }
    else {
        return Compound.find("from Compound a where a.inchiHashKey.completeKey = ?", [value],params)
    }
  }

/**
 * can return 0 - n
 */
  @Queryable(name = "skeleton")
  Collection<Compound> lookupByPartialInchiKey(def value, Map params = [:]) {
    if (value instanceof List){
        return Compound.findAll("from Compound a where a.inchiHashKey.firstBlock in (:varList) ", ["varList" : value],params);
     }
    else {
      return Compound.findAll("from Compound a where a.inchiHashKey.firstBlock = ?", [value],params)
    }
  }

  
  //TODO : No support on multiple name search
  @Queryable(name = "name")
  Collection<Compound> lookupByName(String value, Map params = [:]) {
    return Compound.executeQuery("Select distinct a from Compound a, Synonym b where a.id = b.compound.id and LOWER(b.name) like ?", [value.toLowerCase()],params)
  }

  @Queryable(name = "hmdb")
  Collection<Compound> lookupByHMDB(def value, Map params = [:]) {
    if (value instanceof List){
        return Compound.executeQuery("Select a from Compound a, HMDB b where a.id = b.compound.id and b.hmdbId in (:varList) ", ["varList" : value],params);
     }
    else {
      return Compound.executeQuery("Select a from Compound a, HMDB b where a.id = b.compound.id and b.hmdbId = ?", [value],params)
    }
  }


  @Queryable(name = "lipidmap")
  Collection<Compound> lookupByLipidMapId(def value, Map params = [:]) {
    if (value instanceof List){
        return Compound.executeQuery("Select a from Compound a, LipidMap b where a.id = b.compound.id and b.lipidMapId in (:varList) ", ["varList" : value],params);
     }
    else {
      return Compound.executeQuery("Select a from Compound a, LipidMap b where a.id = b.compound.id and b.lipidMapId = ?", [value],params)
    }
  }


  @Queryable(name = "chebi")
  Collection<Compound> lookupByChebiId(def value, Map params = [:]) {
    if (value instanceof List){
        return Compound.executeQuery("Select a from Compound a, Chebi b where a.id = b.compound.id and b.chebiId in (:varList) ", ["varList" : value],params);
     }
    else {
      return Compound.executeQuery("Select a from Compound a, Chebi b where a.id = b.compound.id and b.chebiId = ?", [value],params)
    }
  }

/**
 * looks up the compound for a single hit
 * can return 0 - n
 */
  Collection<Compound> lookupByHit(Hit hit, Map params = [:]) {
    //contains our results
    Collection<Compound> result = new HashSet<Compound>()

    //find out which type our hit is
    switch (hit.type) {
      case hit.INCHI:
        log.debug("detected inchi...")
        result.add(lookupByInchi(hit.value,params))
        break
      case hit.INCHI_KEY:
        log.debug("detected inchi key...")

        result.add(lookupByInchiKey(hit.value,params))
        break
      case hit.CAS:
        log.debug("detected cas...")

        result = lookupByCas(hit.value,params)
        break
      case hit.KEGG:
        log.debug("detected kegg...")

        result = lookupByKegg(hit.value,params)
        break
      case hit.COMPOUND_ID:
              log.debug("detected compound id...")

              result.add(lookupByCompoundId(hit.compoundId))
              break
      case hit.OSCAR:
              log.debug("detected oscar hit...")

              result = lookupByName(hit.value,params)
              break

      default:
        break
    }

    //print our model
    log.debug("model: ${result}")

    //return the result
    return result
  }

}
